﻿namespace OutLook.Model.Domains
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using OutLook.Model.Infrastructure;

    /// <summary>
    /// Description for output
    /// </summary>
    [DataContract]
    public class Output : IAssignable, IEquatable<Output>, IValidatable
    {
        #region Constructors

        /// <summary>
        /// Initializes new default instance of Output
        /// </summary>
        private Output() : this(new ChemicalСombination[0], 0.0d, new ChemicalСombination[0], 0.0d) { }

        /// <summary>
        /// Initializes new  instance of Output
        /// </summary>
        /// <param name="castIronComposition">TODO:Nastya</param>
        /// <param name="castIronWeight">TODO:Nastya</param>
        /// <param name="slagComposition">TODO:Nastya</param>
        /// <param name="slagWeight">TODO:Nastya</param>
        private Output(IEnumerable<ChemicalСombination> castIronComposition, double castIronWeight, IEnumerable<ChemicalСombination> slagComposition, double slagWeight)
        {
            m_CastIronComposition = new List<ChemicalСombination>(castIronComposition);
            m_CastIronWeight = castIronWeight;
            m_SlagComposition = new List<ChemicalСombination>(slagComposition);
            m_SlagWeight = slagWeight;
            LElemnets = new List<Coupla>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Castiron chemical composition
        /// </summary>
        [DataMember(Order = 1)]
        public List<ChemicalСombination> CastIronComposition
        {
            get { return m_CastIronComposition; }
            set { m_CastIronComposition = value; }
        }

        /// <summary>
        /// Castiron weight
        /// </summary>
        [DataMember(Order = 2)]
        public double CastIronWeight
        {
            get { return m_CastIronWeight; }
            set { m_CastIronWeight = value; }
        }

        /// <summary>
        /// Slag chemical composition
        /// </summary>
        [DataMember(Order = 3)]
        public List<ChemicalСombination> SlagComposition
        {
            get { return m_SlagComposition; }
            set { m_SlagComposition = value; }
        }

        /// <summary>
        /// Slag weight
        /// </summary>
        [DataMember(Order = 4)]
        public double SlagWeight
        {
            get { return m_SlagWeight; }
            set { m_SlagWeight = value; }
        }

        /// <summary>
        /// Basic 1 (CaO/SiO2) of slag (calculation property)
        /// </summary>
        [DataMember(Order = 5)]
        public double Basic1 { get; set; }

        /// <summary>
        /// Basic 2 ((CaO+MgO)/SiO2) of slag (calculation property)
        /// </summary>
        [DataMember(Order = 6)]
        public double Basic2 { get; set; }

        /// <summary>
        /// Basic 3 (Al2O3/MgO) of slag (calculation property)
        /// </summary>
        [DataMember(Order = 7)]
        public double Basic3 { get; set; }

        [DataMember(Order=12)]
        public List<Coupla> LElemnets { get; set; }

        /// <summary>
        /// De of slag (calculation property)
        /// </summary>
        [DataMember(Order = 8)]
        public double SlagDe { get; set; }

        /// <summary>
        /// Ro of slag (calculation property)
        /// </summary>
        [DataMember(Order = 9)]
        public double SlagRo { get; set; }

        /// <summary>
        /// De of metall (calculation property)
        /// </summary>
        [DataMember(Order = 10)]
        public double MetallZy { get; set; }

        /// <summary>
        /// Ro of metall (calculation property)
        /// </summary>
        [DataMember(Order = 11)]
        public double Metalld { get; set; }

        /// <summary>
        /// Viscosity of slag for tempreture 1450 C (calculation property)
        /// </summary>
        [DataMember(Order = 12)]
        public double ViscosityFor1450C { get; set; }

        /// <summary>
        /// Viscosity of slag for tempreture 1500 C (calculation property)
        /// </summary>
        [DataMember(Order = 13)]
        public double ViscosityFor1500C { get; set; }

        /// <summary>
        /// Viscosity of slag for tempreture 1550 C (calculation property)
        /// </summary>
        [DataMember(Order = 14)]
        public double ViscosityFor1550C { get; set; }

        /// <summary>
        /// Surface Tension of slag for tempreture 1500 C (calculation property)
        /// </summary>
        [DataMember(Order = 15)]
        public double SurfaceTension1500С { get; set; }

        /// <summary>
        /// Enthalpy of slag (calculation property)
        /// </summary>
        [DataMember(Order = 15)]
        public double Enthalpy { get; set; }

        /// <summary>
        /// the temperature of initial melting of slag(calculation property)
        /// </summary>
        [DataMember(Order = 16)]
        public double InitialMelting { get; set; }
        
        /// <summary>
        /// the temperature of crystallization of slag(calculation property)
        /// </summary>
        [DataMember(Order = 17)]
        public double TemperatureOfCrystallization { get; set; }

        /// <summary>
        /// Sulfur absorbing capacity of slag(calculation property)
        /// </summary>
        [DataMember(Order = 18)]
        public double SulfurAbsorbingCapacity { get; set; }

        /// <summary>
        /// Ls actual(calculation property)
        /// </summary>
        [DataMember(Order = 19)]
        public double LsActual { get; set; }

        /// <summary>
        /// Ls balance(calculation property)
        /// </summary>
        [DataMember(Order = 20)]
        public double LsBalance { get; set; }
        	
        /// <summary>
        /// Ls Relatively (calculation property)
        /// </summary>
        [DataMember(Order = 21)]
        public double LsRelatively { get; set; }

        
        #endregion

        #region Factory Method

        /// <summary>
        /// Creates new default instance of Output
        /// </summary>
        /// <returns>New default instance of Output</returns>
        public static Output Create()
        {
            return new Output();
        }

        /// <summary>
        /// Creates new  instance of Output
        /// </summary>
        /// <param name="castIronComposition">TODO:Nastya</param>
        /// <param name="castIronWeight">TODO:Nastya</param>
        /// <param name="slagComposition"></param>
        /// <param name="slagWeight">TODO:Nastya</param>
        /// <returns>New instance of Output</returns>
        /// <remarks>Can return NULL if input arguments were wrong</remarks>
        public static Output Create(IEnumerable<ChemicalСombination> castIronComposition, double castIronWeight, IEnumerable<ChemicalСombination> slagComposition, double slagWeight)
        {
            if (castIronComposition == null || slagComposition == null)
                return null;

            return new Output(castIronComposition, castIronWeight, slagComposition, slagWeight);
        }

        #endregion

        #region IAssignable Implementation

        /// <summary>
        /// Assigns values of current output to the argument object
        /// </summary>
        /// <param name="obj">Object to assign to</param>
        /// <exception cref="ArgumentNullException">If object to assign to is nullable</exception>
        /// <exception cref="NotSupportedException">If object to assign to doesn't fit</exception>
        public void AssignTo(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException();

            if (obj is Output)
            {
                Output output = obj as Output;
                output.CastIronWeight = CastIronWeight;
                output.SlagWeight = SlagWeight;
                output.SlagComposition.Clear();
                output.CastIronComposition.Clear();

                foreach (ChemicalСombination item in CastIronComposition)
                {
                    output.CastIronComposition.Add(item);
                }

                foreach (ChemicalСombination item in SlagComposition)
                {
                    output.SlagComposition.Add(item);
                }

                return;
            }

            throw new NotSupportedException();
        }

        #endregion

        #region IEquatable<Output> Implementation

        /// <summary>
        /// Compares current Output with another one
        /// </summary>
        /// <param name="other">Another Output</param>
        /// <returns>True - If current Output and another one are equal, otherwise -False</returns>
        public bool Equals(Output other)
        {
            if (other == null)
                return false;

            if (CastIronWeight != other.CastIronWeight || SlagWeight != other.SlagWeight)
                return false;

            if (CastIronComposition.Count != other.CastIronComposition.Count || SlagComposition.Count != other.SlagComposition.Count)
                return false;

            bool result = true;
            for (int i = 0; i < CastIronComposition.Count; i++)
            {
                if (!CastIronComposition[i].Equals(other.CastIronComposition[i]))
                {
                    result = false;
                    break;
                }
            }

            for (int i = 0; i < SlagComposition.Count; i++)
            {
                if (!result || !SlagComposition[i].Equals(other.SlagComposition[i]))
                {
                    result = false;
                    break;
                }
            }

            return result;
        }

        #endregion

        #region IValidatable Implementation

        /// <summary>
        /// Validates Output
        /// </summary>
        /// <returns>True - if current Output is valid, otherwise - false</returns>
        public bool Validate()
        {
            ///TODO:Output VALIDATION 
            if (m_CastIronComposition.Count == 0 || m_SlagComposition.Count == 0)
                return false;

            return true;
        }

        #endregion

        #region Fields

        private List<ChemicalСombination> m_CastIronComposition = null;
        private double m_CastIronWeight = default(double);
        private List<ChemicalСombination> m_SlagComposition = null;
        private double m_SlagWeight = default(double);

        #endregion
    }
}
